/*
 * Copyright 2011-2012 Rivoli team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.rivoli.model

import com.googlecode.rivoli.RivoliException

class Element {
	final ElementDescriptor descriptor
	private Map<String, Object> attributes
	private List<Element> children

	Element(ElementDescriptor descriptor) {
		this.descriptor = descriptor
		attributes = [:]
		children = []
	}

	void setAttribute(String name, Object value) throws RivoliException {
		verifyAttribute(name, value)

		attributes[name] = value
	}

	Object getAttribute(String name) throws RivoliException {
		verifyAttribute(name)

		attributes[name]
	}

	private void verifyAttribute(String name, Object value = null) throws RivoliException {
		AttributeDescriptor attributeDescriptor = findAttributeDescriptor(descriptor, name)

		if (!attributeDescriptor) {
			throw new RivoliException("attribute with name '${name}' does not exist")
		}

		if ((value != null) && !(value in attributeDescriptor.type)) {
			throw new RivoliException("attribute '${name}' has expected type: ${attributeDescriptor.type.name} but ${value.class.name} was given")
		}
	}

	private AttributeDescriptor findAttributeDescriptor(ElementDescriptor elementDescriptor, String name) {
		elementDescriptor ? (elementDescriptor.attributeDescriptors.find {it.name == name} ?: findAttributeDescriptor(elementDescriptor.parent, name)) : null
	}

	void addChild(Element child) throws RivoliException {
		if (!isContainerFor(descriptor, child.descriptor)) {
			throw new RivoliException("element ${descriptor.name} is not a container for ${child.descriptor.name}")
		}

		children << child
	}

	private boolean isContainerFor(ElementDescriptor containerDescriptor, ElementDescriptor elementDescriptor) {
		elementDescriptor && ((containerDescriptor.containerFor == elementDescriptor) || isContainerFor(containerDescriptor, elementDescriptor.parent))
	}

	List<Element> getChildren() {
		Collections.unmodifiableList(children)
	}
}